package com.qhyt.generate;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.qhyt.utils.ExportUtils;
import com.qhyt.framework.util.annotation.TrimString;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.swing.filechooser.FileSystemView;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import javax.validation.Valid;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author jie
 * @date 2020/10/16 -13:51
 */
@Controller
@RequestMapping(path = "generate")
@Api(description = "生成代码")
public class TestGenerateController {
    @Resource
    private JdbcTemplate jdbcTemplate;


    private static final String ABSOLUTE_PATH = System.getProperty("user.dir");
    private static final String PROJECT = "";
    private static final String COMPANY_NAME = "qhyt";

    @TrimString
    @ResponseBody
    @ApiOperation("java_file")
    @RequestMapping(path = "javaFile", method = RequestMethod.POST)
    public void javaFile(@RequestBody @Valid GenerateDto generateDto) throws Exception {

        String tableName = generateDto.getTableName();
        String description = generateDto.getControllerDescription();
        String pkPrefix = generateDto.getPkPrefix();
        boolean guidFlag = generateDto.getGuidFlag();
        int templateIndex = generateDto.getTemplateIndex();
        List<String> parameterClassNameList = generateDto.getParameterClassNameList();

        if (templateIndex == 1) {
            // 基础资料类型 模板
            baseTemplate(tableName, description, pkPrefix, guidFlag);

        } else {

            // 一对多复杂类型 模板
            mutipartDetailsTemplate(tableName, description, pkPrefix, guidFlag, parameterClassNameList);

        }

    }


    @ApiOperation("生成接口开发文档")
    @RequestMapping(path = "interface_word/{tableName}", method = RequestMethod.GET)
    public void interfaceWord(@PathVariable String tableName, HttpServletResponse response) {
        List<TableEntity> tableEntityList = this.getEntityFromTableName(tableName);
        Workbook workbook = ExcelExportUtil.exportBigExcel(new ExportParams("生成接口开发文档", tableName), TableEntity.class, tableEntityList);
        ExportUtils.ioExport(response, workbook, tableName+"开发文档.xlsx");


    }

    @ResponseBody
    @ApiOperation("生成code枚举")
    @RequestMapping(path = "generate_enum", method = RequestMethod.POST)
    public void generateEnum(String code) throws Exception {
        List<SysCodeSub> sysCodeSubList = searchFromSysCode(code);
//        List<SysCodeSub> sysCodeSubList = searchFromSysCode2();
        if (CollectionUtils.isEmpty(sysCodeSubList)) {
            System.out.println("你字典值输错了");
        }
        generateCodeEnum(sysCodeSubList, code);
    }


    private void mutipartDetailsTemplate(String tableName, String description, String pkPrefix, boolean guidFlag, List<String> parameterClassNameList) throws Exception {

        String entity = GenerateUtils.prefixLetterCap(GenerateUtils.underlineToHump(tableName));
        // 生成实体
        List<TableEntity> tableEntityList = getEntityFromTableName(tableName);
        generateEntityCode(entity, tableEntityList, guidFlag);

//        // 生成pageDto
//        String pageDtoClassPath = generatePageDtoCode(entity);
//        Class<?> pageDtoClassType = Class.forName(pageDtoClassPath);
//        Field[] pageDtoFields = pageDtoClassType.getDeclaredFields();
//
//        generatePageVoCode(entity);
//
//        // 表头
//        List<Parameters> headerList = getParameterList(3);
//        // 明细1
//        List<Parameters> details1 = getParameterList(4);
//        // 明细2
//        List<Parameters> details2 = getParameterList(5);
//
//        generateAddOrUpdateDtoCode(entity, true, parameterClassNameList, headerList, details1, details2);
//        generateDetailsVoCode(entity, parameterClassNameList, headerList, details1, details2);
//
//        generateMapperCode(entity);
//        generateDaoCode(entity, guidFlag, pkPrefix, pageDtoFields);
//
//        generateServiceCode(entity, true);
//        generateServiceImplCode(entity, guidFlag, true);
//        generateControllerCode(entity, description, true);

    }

    private void baseTemplate(String tableName, String description, String pkPrefix, boolean guidFlag) throws Exception {

        String entity = GenerateUtils.prefixLetterCap(GenerateUtils.underlineToHump(tableName));
        // 生成实体
        List<TableEntity> tableEntityList = getEntityFromTableName(tableName);
        generateEntityCode(entity, tableEntityList, guidFlag);

        // 生成pageDto
        String pageDtoClassPath = generatePageDtoCode(entity);
        Class<?> pageDtoClassType = Class.forName(pageDtoClassPath);
        Field[] pageDtoFields = pageDtoClassType.getDeclaredFields();

        generatePageVoCode(entity);
        generateAddOrUpdateDtoCode(entity, false, null, null, null);

        generateMapperCode(entity);
        generateDaoCode(entity, guidFlag, pkPrefix, pageDtoFields);

        generateServiceCode(entity, false);
        generateServiceImplCode(entity, guidFlag, false);
        generateControllerCode(entity, description, false);
    }


    @ApiOperation("droplist")
    @RequestMapping(path = "to_droplist", method = RequestMethod.GET)
    public String toDroplist(ModelMap model) {

        String entity = "EmployeeInfo";
        String searchField = "name";

        model.addAttribute("entity", entity);
        model.addAttribute("searchField", searchField);

        return "droplist";
    }

    @ApiOperation("check_add")
    @RequestMapping(path = "check_add", method = RequestMethod.GET)
    @ResponseBody
    public void checkAdd(String entity, String field) throws Exception {

        String fileStr = GenerateUtils.getCheckAddStr(entity, field);

        createOutputStream(getCodePath("javaCodeTempFile.txt"), fileStr);

    }


    private List<SysCodeSub> searchFromSysCode(String code) {
        String tableSql = "SELECT * FROM sys_code_sub WHERE id = '" + code + "'";
        List<Map<String, Object>> tableMaps = jdbcTemplate.queryForList(tableSql);

        List<SysCodeSub> sysCodeSubList = new ArrayList<>();
        for (Map<String, Object> tableMap : tableMaps) {

            SysCodeSub sysCodeSub = new SysCodeSub();
            sysCodeSub.setSubCode(tableMap.get("sub_code").toString());
            sysCodeSub.setNameCn(tableMap.get("name_cn").toString());
            sysCodeSubList.add(sysCodeSub);
        }

        return sysCodeSubList;
    }

    private List<SysCodeSub> searchFromSysCode2() {
        String tableSql = "SELECT name_cn,trace_config_value as sub_code FROM `order_express_trace_config` ";
        List<Map<String, Object>> tableMaps = jdbcTemplate.queryForList(tableSql);

        List<SysCodeSub> sysCodeSubList = new ArrayList<>();
        for (Map<String, Object> tableMap : tableMaps) {

            SysCodeSub sysCodeSub = new SysCodeSub();
            sysCodeSub.setSubCode(tableMap.get("sub_code").toString());
            sysCodeSub.setNameCn(tableMap.get("name_cn").toString());
            sysCodeSubList.add(sysCodeSub);
        }

        return sysCodeSubList;
    }

    private List<TableEntity> getEntityFromTableName(String tableName) {
        String tableSql = "select * from information_schema.columns where TABLE_NAME = '" + tableName + "' and TABLE_SCHEMA = 'dbo' ;";
        List<Map<String, Object>> tableMaps = jdbcTemplate.queryForList(tableSql);

        List<TableEntity> tableEntityList = new ArrayList<>();
        for (Map<String, Object> tableMap : tableMaps) {
            TableEntity tableEntity = new TableEntity();
            String dateType = tableMap.get("DATA_TYPE").toString();
            String columnName = tableMap.get("COLUMN_NAME").toString();

            String javaType = convertToJavaType(dateType);
            String javaField = GenerateUtils.underlineToHump(columnName);

            tableEntity.setFieldName(null);
            tableEntity.setField(javaField);
            tableEntity.setType(javaType);
            tableEntityList.add(tableEntity);
        }

        return tableEntityList;
    }

    private String convertToJavaType(String dateType) {

        if (dateType.equalsIgnoreCase(Constans.INT)) {
            return "Integer";
        }

        if (dateType.equalsIgnoreCase(Constans.VARCHAR)) {
            return "String";
        }

        if (dateType.equalsIgnoreCase(Constans.DECIMAL)) {
            return "BigDecimal";
        }

        if (dateType.equalsIgnoreCase(Constans.DATETIME) || dateType.equalsIgnoreCase(Constans.DATE)) {
            return "Date";
        }

        if (dateType.equalsIgnoreCase(Constans.DOUBLE)) {
            return "Double";
        }

        return "String";
    }

    /**
     * 生成mapper代码
     *
     * @param entity 实体
     * @throws IOException
     */
    private void generateMapperCode(String entity) throws IOException {
        String filePath = getFilePath(entity, "mapper", "Mapper.java");
        createFile(filePath);

        String fileStr = GenerateUtils.getMapperStr(entity, COMPANY_NAME);
        createOutputStream(filePath, fileStr);

        compiler(filePath);
    }


    /**
     * 生成dao代码
     *
     * @param entity 实体
     * @throws IOException
     */
    private void generateDaoCode(String entity, boolean guidFlag, String pkPrefix, Field[] fields) throws IOException {
        String filePath = getFilePath(entity, "dao", "Dao.java");
        createFile(filePath);

        List<Parameters> parameterList = getParameterList(1);

        String fileStr = GenerateUtils.getDaoStr(entity, COMPANY_NAME, guidFlag, pkPrefix, fields, parameterList);
        createOutputStream(filePath, fileStr);
        compiler(filePath);
    }

    /**
     * 生成service代码
     *
     * @param entity 实体
     * @throws IOException
     */
    private void generateServiceCode(String entity, boolean detailsFlag) throws IOException {
        String filePath = getFilePath(entity, "service", "Service.java");
        createFile(filePath);

        String fileStr = GenerateUtils.getServiceStr(entity, COMPANY_NAME, detailsFlag);
        createOutputStream(filePath, fileStr);
        compiler(filePath);
    }

    /**
     * 生成serviceImpl代码
     *
     * @param entity 实体
     * @throws IOException
     */
    private void generateServiceImplCode(String entity, boolean guidFlag, boolean detailsFlag) throws IOException {
        String type = "service";
        String filePackage = getFilePackage(entity);
        String filePath = ABSOLUTE_PATH + "\\" + PROJECT + "\\src\\main\\java\\com\\" + COMPANY_NAME + "\\" + type + "\\" + filePackage + "\\" + "impl\\" + entity + "ServiceImpl.java";
        createFile(filePath);

        List<Parameters> parameterList = getParameterList(2);

        String fileStr;
        if (detailsFlag) {
            fileStr = GenerateUtils.getServiceImplStr2(entity, COMPANY_NAME, parameterList, guidFlag);

        } else {
            fileStr = GenerateUtils.getServiceImplStr(entity, COMPANY_NAME, parameterList, guidFlag);

        }

        createOutputStream(filePath, fileStr);
//        compiler(filePath);
    }


    /**
     * 生成controller代码
     *
     * @param entity      实体
     * @param description 描述
     * @throws IOException
     */
    private void generateControllerCode(String entity, String description, boolean detailsFlag) throws IOException {
        String filePath = getFilePath(entity, "controller", "Controller.java");
        createFile(filePath);

        String fileStr = GenerateUtils.getControllerStr(entity, COMPANY_NAME, description, detailsFlag);
        createOutputStream(filePath, fileStr);
        compiler(filePath);
    }

    /**
     * 生成pageDto代码
     *
     * @param entity 实体
     * @throws IOException
     */
    private String generatePageDtoCode(String entity) throws Exception {
        String type = "dto";
        String filePackage = getFilePackage(entity);
        String filePath = ABSOLUTE_PATH + "\\" + PROJECT + "\\src\\main\\java\\com\\" + COMPANY_NAME + "\\" + type + "\\" + filePackage + "\\" + entity + "PageDto.java";
        createFile(filePath);

        List<Parameters> parameterList = getParameterList(1);

        String fileStr = GenerateUtils.getPageDtoStr(entity, COMPANY_NAME, parameterList);
        createOutputStream(filePath, fileStr);
        // 编译
        compiler(filePath);
        return "com." + COMPANY_NAME + "." + type + "." + filePackage + "." + entity + "PageDto";
    }

    private String getFilePackage(String entity) {
        return GenerateUtils.getPackagePrefix(entity);
    }

    /**
     * 生成pageVo代码
     *
     * @param entity 实体
     * @throws IOException
     */
    private void generatePageVoCode(String entity) throws IOException {
        String filePath = getFilePath(entity, "vo", "PageVo.java");
        createFile(filePath);

        List<Parameters> parameterList = getParameterList(0);

        String fileStr = GenerateUtils.getPageVoStr(entity, COMPANY_NAME, parameterList);
        createOutputStream(filePath, fileStr);

        compiler(filePath);
    }

    private String getFilePath(String entity, String type, String tempStr) {
        String filePackage = getFilePackage(entity);
        return ABSOLUTE_PATH + "\\" + PROJECT + "\\src\\main\\java\\com\\" + COMPANY_NAME + "\\" + type + "\\" + filePackage + "\\" + entity + tempStr;
    }

    /**
     * 生成AddOrUpdateDto代码
     *
     * @param entity 实体
     * @throws IOException
     */
    private void generateAddOrUpdateDtoCode(String entity, boolean detailsFlag, List<String> parameterClassNameList, List<Parameters>... parametersList) throws IOException {
        String filePath = getFilePath(entity, "dto", "AddOrUpdateDto.java");
        createFile(filePath);

        List<Parameters> parameterList = getParameterList(2);

        String fileStr;
        if (detailsFlag) {
            fileStr = GenerateUtils.getAddOrUpdateDtoStr(entity, COMPANY_NAME, parameterClassNameList, parametersList);

        } else {
            fileStr = GenerateUtils.getAddOrUpdateDtoStr(entity, COMPANY_NAME, parameterList);

        }

        createOutputStream(filePath, fileStr);

        compiler(filePath);
    }

    /**
     * 生成detailsVo代码
     */
    private void generateDetailsVoCode(String entity, List<String> parameterClassNameList, List<Parameters>... parametersList) throws IOException {
        String filePath = getFilePath(entity, "vo", "DetailsVo.java");
        createFile(filePath);

        String fileStr = GenerateUtils.getDetailsVoStr(entity, COMPANY_NAME, parameterClassNameList, parametersList);
        createOutputStream(filePath, fileStr);

        compiler(filePath);
    }

    /**
     * 生成Entity代码
     *
     * @param entity 实体
     * @throws IOException
     */
    private String generateEntityCode(String entity, List<TableEntity> tableEntityList, boolean guidFlag) throws Exception {

        String type = "model";
        String filePackage = getFilePackage(entity);
        String filePath = ABSOLUTE_PATH + "\\" + PROJECT + "\\src\\main\\java\\com\\" + COMPANY_NAME + "\\" + type + "\\" + filePackage + "\\" + entity + ".java";
        createFile(filePath);

        String fileStr = GenerateUtils.getEntityStr(entity, COMPANY_NAME, tableEntityList, guidFlag);
        createOutputStream(filePath, fileStr);
        // 编译
        compiler(filePath);
        return "com." + COMPANY_NAME + "." + type + "." + filePackage + "." + entity;
    }

    /**
     * 生成enum
     */
    private void generateCodeEnum(List<SysCodeSub> sysCodeSubList, String code) throws Exception {
        String humpCode = GenerateUtils.underlineToHump(code);
        String enumName = GenerateUtils.prefixLetterCap(humpCode);
        String filePath = ABSOLUTE_PATH + "\\" + PROJECT + "\\src\\main\\java\\com\\" + COMPANY_NAME + "\\enums\\" + enumName + "Enum.java";
        createFile(filePath);

        String fileStr = GenerateUtils.getEnumStr(sysCodeSubList, enumName, COMPANY_NAME);
        createOutputStream(filePath, fileStr);
        // 编译
        compiler(filePath);
    }

    /**
     * 读取Excel获取类型和字段
     *
     * @param startSheetIndex startSheetIndex
     */
    private List<Parameters> getParameterList(int startSheetIndex) {
        File file = new File(getCodePath("代码生成.xlsx"));
        ImportParams params = new ImportParams();
//        params.setKeyIndex(0);
        params.setStartSheetIndex(startSheetIndex);
        return ExcelImportUtil.importExcel(file, Parameters.class, params);
    }


    private void compiler(String filePath) {
        //动态编译
        JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
        int status = javac.run(null, null, null, "-d", ABSOLUTE_PATH + "\\" + PROJECT + "\\target\\classes", filePath);
        if (status != 0) {
            System.out.println("没有编译成功！");
        } else {
            System.out.println("编译成功！");
        }
    }

    /**
     * 获取桌面路径
     *
     * @param fileName 文件名
     */
    private String getCodePath(String fileName) {
        File desktopDir = FileSystemView.getFileSystemView().getHomeDirectory();
        return desktopDir.getAbsolutePath() + File.separator + fileName;
    }

    /**
     * 写入文件
     *
     * @param filePath 文件路径
     * @param fileStr  文件生成字符串代码
     * @throws IOException
     */
    private void createOutputStream(String filePath, String fileStr) throws IOException {
        //创建一个文件字节输出流
        FileOutputStream out = new FileOutputStream(filePath);
        byte[] bytes = fileStr.getBytes();
        out.write(bytes, 0, bytes.length);
        out.close();
    }

    /**
     * 新建文件
     *
     * @param filePath 文件路径
     * @throws IOException
     */
    private void createFile(String filePath) throws IOException {
        File file = new File(filePath);

        if (!file.exists()) {
            File parentFile = file.getParentFile();
            String destDirName = parentFile.getPath();
            if (parentFile.exists()) {
                System.out.println("创建目录" + destDirName + "失败，目标目录已经存在");
                return ;
            }
            if (!destDirName.endsWith(File.separator)) {
                destDirName = destDirName + File.separator;
            }
            //创建目录
            if (parentFile.mkdirs()) {
                System.out.println("创建目录" + destDirName + "成功！");
            } else {
                System.out.println("创建目录" + destDirName + "失败！");
            }

            file.createNewFile();
        }
    }


}
